package com.lwl.blue.seven.test7;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lwl.blue.commont.base.JsonResult;
import com.lwl.blue.commont.base.PageParam;
import com.lwl.blue.commont.base.PageResult;
import com.lwl.blue.seven.constants.ContractStatus;
import com.lwl.blue.seven.dao.Contract7Dao;
import com.lwl.blue.seven.dao.ContractItem7Dao;
import com.lwl.blue.seven.dto.*;
import com.lwl.blue.seven.entity.Contract7;
import com.lwl.blue.seven.entity.ContractItem7;
import com.lwl.blue.seven.service.Contract7Service;
import com.lwl.blue.seven.service.impl.Contract7ServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.*;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;

/**
 * 合同服务实现类测试类
 * 用于测试Contract7ServiceImpl中的各个业务方法
 */
@ExtendWith(MockitoExtension.class)
//@SpringBootTest
public class Contract7ServiceImplTest {

    // 注入被测试的服务类，并将@Mock标记的依赖注入其中
    @InjectMocks
    private Contract7ServiceImpl contract7Service;

    // 模拟Contract7Dao数据访问层
    @Mock
    private Contract7Dao contract7Dao;

    // 模拟ContractItem7Dao数据访问层
    @Mock
    private ContractItem7Dao contractItem7Dao;

    /**
     * 测试前初始化Mock HTTP请求上下文
     * 为测试提供必要的请求上下文环境
     */
    @BeforeEach
    public void setUp() {
        // 设置 Mock HTTP 请求上下文
        MockHttpServletRequest request = new MockHttpServletRequest();
        RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
    }

    /**
     * 测试查询合同列表方法
     * 验证Contract7ServiceImpl.queryContractList方法是否正常工作
     */
    @Test
    public void testQueryContractList() {
        // 准备测试数据
        Contract7QueryDTO queryDTO = new Contract7QueryDTO();
        queryDTO.setKeyword("TEST001");
        queryDTO.setSupplierId(1);
        queryDTO.setStatus(ContractStatus.DRAFT.getCode());

        PageParam<Contract7QueryDTO> pageParam = new PageParam<>();
        pageParam.setParam(queryDTO);
        pageParam.setPageNum(1);
        pageParam.setPageSize(10);

        // 创建分页对象和测试数据
        IPage<Contract7> mockPage = new Page<>(1, 10, 1L);
        List<Contract7> contractList = new ArrayList<>();
        Contract7 contract = new Contract7();
        contract.setContractId(1L);
        contract.setContractNumber("TEST001");
        contract.setContractName("测试合同");
        contract.setSupplierId(1);
        contract.setStatus(ContractStatus.DRAFT.getCode());
        contract.setIsDeleted(0);
        contract.setCreateUserId(1L);
        contract.setCreateTime(new Date());
        contract.setTotalAmount(new BigDecimal("1000.00"));
        contractList.add(contract);
        mockPage.setRecords(contractList);

        // 使用 spy 来部分 mock service
        Contract7ServiceImpl spyService = spy(contract7Service);

        // 关键：直接 Mock page() 方法，避免 MyBatis Plus 内部调用
        doReturn(mockPage).when(spyService).page(any(Page.class), any(LambdaQueryWrapper.class));

        // 执行服务方法
        PageResult<Contract7ListVO> result = spyService.queryContractList(pageParam, 1L);

        // 验证结果
        assertNotNull(result); // 验证返回结果不为空
        assertTrue(result.isSuccess()); // 验证结果状态为成功
        assertEquals(1, result.getCount()); // 验证总记录数为1
        assertEquals(1, result.getData().size()); // 验证数据列表大小为1

        Contract7ListVO resultVo = result.getData().get(0);
        assertEquals("TEST001", resultVo.getContractNumber()); // 验证合同编号一致
        assertEquals("测试合同", resultVo.getContractName()); // 验证合同名称一致
        assertEquals(ContractStatus.DRAFT.getCode(), resultVo.getStatus()); // 验证状态码一致
        assertEquals("准备", resultVo.getStatusName()); // 验证状态名称一致

        // 验证权限标识（根据 setPermissionFlags 逻辑）
        assertTrue(resultVo.getCanEdit()); // 验证编辑权限为true
        assertTrue(resultVo.getCanDelete()); // 验证删除权限为true
        assertTrue(resultVo.getCanSubmit()); // 验证提交权限为true
        assertFalse(resultVo.getCanAudit()); // 验证审核权限为false

        // 验证调用
        verify(spyService, times(1)).page(any(Page.class), any(LambdaQueryWrapper.class));
    }

    /**
     * 测试获取合同详情方法
     * 验证Contract7ServiceImpl.getContractDetail方法是否正常工作
     */
    @Test
    public void testGetContractDetail() {
        // 准备测试数据
        Long contractId = 1L;
        Contract7 contract = new Contract7();
        contract.setContractId(contractId);
        contract.setContractNumber("TEST001");
        contract.setContractName("测试合同");
        contract.setStatus(ContractStatus.DRAFT.getCode());
        contract.setIsDeleted(0);

        List<Contract7DetailItemDTO> items = new ArrayList<>();
        Contract7DetailItemDTO item = new Contract7DetailItemDTO();
        item.setDetailId(1L);
        item.setContractId(contractId);
        item.setProductName("测试商品");
        items.add(item);

        // Mock DAO方法
        when(contract7Dao.queryById(contractId)).thenReturn(contract); // 模拟查询合同主表数据
        when(contractItem7Dao.queryItemsByContractId(contractId)).thenReturn(items); // 模拟查询合同明细数据

        // 执行测试
        Contract7DetailDTO result = contract7Service.getContractDetail(contractId, 1L);

        // 验证结果
        assertNotNull(result); // 验证返回结果不为空
        assertEquals(contractId, result.getContractId()); // 验证合同ID一致
        assertEquals("准备", result.getStatusName()); // 验证状态名称一致
        assertNotNull(result.getItemList()); // 验证明细列表不为空
        assertEquals(1, result.getItemList().size()); // 验证明细列表大小为1
    }

    /**
     * 测试获取合同详情方法 - 合同不存在的情况
     * 验证Contract7ServiceImpl.getContractDetail方法处理不存在合同的情况
     */
    @Test
    public void testGetContractDetail_NotFound() {
        // 准备测试数据
        Long contractId = 1L;

        // Mock DAO方法
        when(contract7Dao.queryById(contractId)).thenReturn(null); // 模拟查询不到合同数据

        // 执行测试
        Contract7DetailDTO result = contract7Service.getContractDetail(contractId, 1L);

        // 验证结果
        assertNull(result); // 验证返回结果为空
    }

    /**
     * 测试创建合同方法 - 成功情况
     * 验证Contract7ServiceImpl.createContract方法是否正常工作
     */
    @Test
    void testCreateContract_Success() {
        // 构造DTO
        Contract7CreateDTO createDTO = new Contract7CreateDTO();
        createDTO.setContractNumber("HT20250922001");
        createDTO.setContractName("测试合同");

        List<Contract7CreateItemDTO> items = new ArrayList<>();
        Contract7CreateItemDTO item = new Contract7CreateItemDTO();
        item.setProductId(1001L);
        item.setProductName("测试商品");
        item.setProductCode("P1001");
        item.setProductUnit("件");
        item.setProductPrice(new BigDecimal("10.00"));
        item.setProductQuantity(new BigDecimal("2"));
        items.add(item);
        createDTO.setItemList(items);

        // mock dao 行为
        when(contract7Dao.checkContractNumberExists(anyString(), any())).thenReturn(0); // 模拟合同编号不存在

        // 插入合同时，手动回填主键ID
        when(contract7Dao.insert(any(Contract7.class))).thenAnswer(invocation -> {
            Contract7 c = invocation.getArgument(0);
            c.setContractId(123L); // 模拟数据库生成ID
            return 1;
        });

        when(contractItem7Dao.batchInsert(anyList())).thenReturn(items.size()); // 模拟批量插入明细成功

        // 调用 service
        JsonResult<Long> result = contract7Service.createContract(createDTO, 1L);

        // 验证
        assertTrue(result.isSuccess()); // 验证结果状态为成功
        assertNotNull(result.getData()); // 验证返回数据不为空
        assertEquals(123L, result.getData()); // 验证返回的合同ID为123
    }


    /**
     * 测试创建合同方法 - 合同编号已存在的情况
     * 验证Contract7ServiceImpl.createContract方法处理合同编号重复的情况
     */
    @Test
    public void testCreateContract_ContractNumberExists() {
        // 准备测试数据
        Contract7CreateDTO createDTO = new Contract7CreateDTO();
        createDTO.setContractNumber("TEST001");
        createDTO.setContractName("测试合同");
        createDTO.setSupplierId(1);
        createDTO.setTransactionType("采购");

        List<Contract7CreateItemDTO> itemList = new ArrayList<>();
        Contract7CreateItemDTO item = new Contract7CreateItemDTO();
        item.setProductId(1L);
        item.setProductName("测试商品");
        item.setProductUnit("个");
        item.setProductPrice(new BigDecimal("100.00"));
        item.setProductQuantity(new BigDecimal("2"));
        itemList.add(item);
        createDTO.setItemList(itemList);

        // Mock DAO方法
        when(contract7Dao.checkContractNumberExists(anyString(), any())).thenReturn(1); // 模拟合同编号已存在

        // 执行测试
        JsonResult<Long> result = contract7Service.createContract(createDTO, 1L);

        // 验证结果
        assertFalse(result.isSuccess()); // 验证结果状态为失败
        assertEquals("合同编号已存在", result.getMessage()); // 验证错误信息一致
    }

    /**
     * 测试更新合同方法 - 成功情况
     * 验证Contract7ServiceImpl.updateContract方法是否正常工作
     */
    @Test
    public void testUpdateContract_Success() {
        // 准备测试数据
        Long contractId = 1L;
        Contract7 contract = new Contract7();
        contract.setContractId(contractId);
        contract.setContractNumber("TEST001");
        contract.setContractName("测试合同");
        contract.setStatus(ContractStatus.DRAFT.getCode());
        contract.setCreateUserId(1L);
        contract.setIsDeleted(0);

        Contract7CreateDTO updateDTO = new Contract7CreateDTO();
        updateDTO.setContractNumber("TEST001");
        updateDTO.setContractName("更新测试合同");
        updateDTO.setSupplierId(1);
        updateDTO.setTransactionType("采购");

        List<Contract7CreateItemDTO> itemList = new ArrayList<>();
        Contract7CreateItemDTO item = new Contract7CreateItemDTO();
        item.setProductId(1L);
        item.setProductName("测试商品");
        item.setProductUnit("个");
        item.setProductPrice(new BigDecimal("100.00"));
        item.setProductQuantity(new BigDecimal("2"));
        itemList.add(item);
        updateDTO.setItemList(itemList);

        // Mock DAO方法
        when(contract7Dao.queryById(contractId)).thenReturn(contract); // 模拟查询合同主表数据
        when(contract7Dao.checkContractNumberExists(anyString(), any())).thenReturn(0); // 模拟合同编号不存在
        when(contract7Dao.update(any(Contract7.class))).thenReturn(1); // 模拟更新合同主表成功

        // --- 必需的补充 stub：删除明细与批量插入 ---
        when(contractItem7Dao.deleteByContractId(contractId)).thenReturn(1); // 模拟删除原有明细成功
        when(contractItem7Dao.batchInsert(anyList())).thenAnswer(invocation -> {
            List<?> list = invocation.getArgument(0);
            return list == null ? 0 : list.size(); // 模拟全部插入成功，返回影响行数
        });

        // 执行测试
        JsonResult<Void> result = contract7Service.updateContract(contractId, updateDTO, 1L);

        // 验证结果
        assertTrue(result.isSuccess()); // 验证结果状态为成功
        verify(contract7Dao, times(1)).update(any(Contract7.class)); // 验证更新主表方法被调用一次
        verify(contractItem7Dao, times(1)).deleteByContractId(contractId); // 验证删除明细方法被调用一次
        verify(contractItem7Dao, times(1)).batchInsert(anyList()); // 验证批量插入明细方法被调用一次
    }


    /**
     * 测试删除合同方法 - 成功情况
     * 验证Contract7ServiceImpl.deleteContract方法是否正常工作
     */
    @Test
    public void testDeleteContract_Success() {
        // 准备测试数据
        Long contractId = 1L;
        Contract7 contract = new Contract7();
        contract.setContractId(contractId);
        contract.setContractNumber("TEST001");
        contract.setStatus(ContractStatus.DRAFT.getCode());
        contract.setCreateUserId(1L);
        contract.setIsDeleted(0);

        // Mock DAO方法
        when(contract7Dao.queryById(contractId)).thenReturn(contract); // 模拟查询合同主表数据
        when(contract7Dao.softDeleteById(contractId)).thenReturn(1); // 模拟软删除合同成功

        // 执行测试
        JsonResult<Void> result = contract7Service.deleteContract(contractId, 1L);

        // 验证结果
        assertTrue(result.isSuccess()); // 验证结果状态为成功
        verify(contract7Dao, times(1)).softDeleteById(contractId); // 验证软删除方法被调用一次
    }

    /**
     * 测试提交合同方法 - 成功情况
     * 验证Contract7ServiceImpl.submitContract方法是否正常工作
     */
    @Test
    public void testSubmitContract_Success() {
        // 准备测试数据
        Long contractId = 1L;
        Contract7 contract = new Contract7();
        contract.setContractId(contractId);
        contract.setContractNumber("TEST001");
        contract.setStatus(ContractStatus.DRAFT.getCode());
        contract.setCreateUserId(1L);
        contract.setIsDeleted(0);

        // Mock DAO方法
        when(contract7Dao.queryById(contractId)).thenReturn(contract); // 模拟查询合同主表数据
        when(contract7Dao.updateContractStatus(contractId, ContractStatus.PENDING_AUDIT.getCode(), null)).thenReturn(1); // 模拟更新合同状态为待审核成功

        // 执行测试
        JsonResult<Void> result = contract7Service.submitContract(contractId, 1L);

        // 验证结果
        assertTrue(result.isSuccess()); // 验证结果状态为成功
        assertEquals("提交成功", result.getMessage()); // 验证返回信息一致
        verify(contract7Dao, times(1)).updateContractStatus(contractId, ContractStatus.PENDING_AUDIT.getCode(), null); // 验证更新状态方法被调用一次
    }

    /**
     * 测试审核通过合同方法 - 成功情况
     * 验证Contract7ServiceImpl.approveContract方法是否正常工作
     */
    @Test
    public void testApproveContract_Success() {
        // 准备测试数据
        Long contractId = 1L;
        Contract7 contract = new Contract7();
        contract.setContractId(contractId);
        contract.setContractNumber("TEST001");
        contract.setStatus(ContractStatus.PENDING_AUDIT.getCode());
        contract.setCreateUserId(1L);
        contract.setIsDeleted(0);

        // Mock DAO方法
        when(contract7Dao.queryById(contractId)).thenReturn(contract); // 模拟查询合同主表数据
        when(contract7Dao.updateContractStatus(contractId, ContractStatus.EXECUTING.getCode(), 1L)).thenReturn(1); // 模拟更新合同状态为执行中成功

        // 执行测试
        JsonResult<Void> result = contract7Service.approveContract(contractId, 1L);

        // 验证结果
        assertTrue(result.isSuccess()); // 验证结果状态为成功
        assertEquals("审核通过", result.getMessage()); // 验证返回信息一致
        verify(contract7Dao, times(1)).updateContractStatus(contractId, ContractStatus.EXECUTING.getCode(), 1L); // 验证更新状态方法被调用一次
    }

    /**
     * 测试驳回合同方法 - 成功情况
     * 验证Contract7ServiceImpl.rejectContract方法是否正常工作
     */
    @Test
    public void testRejectContract_Success() {
        // 准备测试数据
        Long contractId = 1L;
        Contract7 contract = new Contract7();
        contract.setContractId(contractId);
        contract.setContractNumber("TEST001");
        contract.setStatus(ContractStatus.PENDING_AUDIT.getCode());
        contract.setCreateUserId(1L);
        contract.setIsDeleted(0);

        // Mock DAO方法
        when(contract7Dao.queryById(contractId)).thenReturn(contract); // 模拟查询合同主表数据
        when(contract7Dao.updateContractStatus(contractId, ContractStatus.REJECTED.getCode(), 1L)).thenReturn(1); // 模拟更新合同状态为已驳回成功

        // 执行测试
        JsonResult<Void> result = contract7Service.rejectContract(contractId, 1L, "测试驳回原因");

        // 验证结果
        assertTrue(result.isSuccess()); // 验证结果状态为成功
        assertEquals("已驳回", result.getMessage()); // 验证返回信息一致
        verify(contract7Dao, times(1)).updateContractStatus(contractId, ContractStatus.REJECTED.getCode(), 1L); // 验证更新状态方法被调用一次
    }

    /**
     * 测试获取合同明细项列表方法
     * 验证Contract7ServiceImpl.getContractItems方法是否正常工作
     */
    @Test
    public void testGetContractItems() {
        // 准备测试数据
        Long contractId = 1L;
        List<Contract7DetailItemDTO> items = new ArrayList<>();
        Contract7DetailItemDTO item = new Contract7DetailItemDTO();
        item.setDetailId(1L);
        item.setContractId(contractId);
        item.setProductName("测试商品");
        items.add(item);

        // Mock DAO方法
        when(contractItem7Dao.queryItemsByContractId(contractId)).thenReturn(items); // 模拟查询合同明细数据

        // 执行测试
        List<Contract7DetailItemDTO> result = contract7Service.getContractItems(contractId);

        // 验证结果
        assertNotNull(result); // 验证返回结果不为空
        assertEquals(1, result.size()); // 验证结果列表大小为1
        assertEquals("测试商品", result.get(0).getProductName()); // 验证商品名称一致
    }

    /**
     * 测试保存合同明细项方法 - 成功情况
     * 验证Contract7ServiceImpl.saveContractItems方法是否正常工作
     */
    @Test
    public void testSaveContractItems_Success() {
        // 准备测试数据
        Long contractId = 1L;
        List<Contract7CreateItemDTO> items = new ArrayList<>();
        Contract7CreateItemDTO item = new Contract7CreateItemDTO();
        item.setProductId(1L);
        item.setProductName("测试商品");
        item.setProductUnit("个");
        item.setProductPrice(new BigDecimal("100.00"));
        item.setProductQuantity(new BigDecimal("2"));
        items.add(item);

        // Mock DAO方法
        when(contractItem7Dao.batchInsert(anyList())).thenReturn(1); // 模拟批量插入明细成功

        // 执行测试
        JsonResult<Void> result = contract7Service.saveContractItems(contractId, items);

        // 验证结果
        assertTrue(result.isSuccess()); // 验证结果状态为成功
        verify(contractItem7Dao, times(1)).batchInsert(anyList()); // 验证批量插入方法被调用一次
    }

    /**
     * 测试保存合同明细项方法 - 明细列表为空的情况
     * 验证Contract7ServiceImpl.saveContractItems方法处理空明细列表的情况
     */
    @Test
    public void testSaveContractItems_EmptyList() {
        // 准备测试数据
        Long contractId = 1L;
        List<Contract7CreateItemDTO> items = new ArrayList<>();

        // 执行测试
        JsonResult<Void> result = contract7Service.saveContractItems(contractId, items);

        // 验证结果
        assertFalse(result.isSuccess()); // 验证结果状态为失败
        assertEquals("商品明细不能为空", result.getMessage()); // 验证错误信息一致
    }

    /**
     * 测试计算合同总金额私有方法
     * 通过反射调用私有方法验证其计算逻辑是否正确
     */
    @Test
    public void testCalculateTotalAmount() {
        List<Contract7CreateItemDTO> items = new ArrayList<>();
        Contract7CreateItemDTO item1 = new Contract7CreateItemDTO();
        item1.setProductPrice(new BigDecimal("100.0000"));
        item1.setProductQuantity(new BigDecimal("2.0000"));
        items.add(item1);

        Contract7CreateItemDTO item2 = new Contract7CreateItemDTO();
        item2.setProductPrice(new BigDecimal("50.0000"));
        item2.setProductQuantity(new BigDecimal("3.0000"));
        items.add(item2);

        try {
            // 拿到真实 class
            Class<?> targetClass = Contract7ServiceImpl.class;
            java.lang.reflect.Method method = targetClass.getDeclaredMethod("calculateTotalAmount", List.class);
            method.setAccessible(true);

            // 调用时传真实对象（代理也可以，因为是同一个实例）
            BigDecimal result = (BigDecimal) method.invoke(contract7Service, items);

            assertEquals(new BigDecimal("350.000000"), result); // 验证计算结果是否正确 (100*2 + 50*3 = 350)
        } catch (Exception e) {
            fail("调用私有方法失败: " + e.getMessage()); // 如果调用失败则标记测试失败
        }
    }

}
